package com.liteng.shadow.core.utils;

public class SnowFlakeGenerator
{
  private static final long START_STMP = 1480166465631L;
  private static final long SEQUENCE_BIT = 12L;
  private static final long MACHINE_BIT = 5L;
  private static final long DATACENTER_BIT = 5L;
  private static final long MAX_DATACENTER_NUM = 31L;
  private static final long MAX_MACHINE_NUM = 31L;
  private static final long MAX_SEQUENCE = 4095L;
  private static final long MACHINE_LEFT = 12L;
  private static final long DATACENTER_LEFT = 17L;
  private static final long TIMESTMP_LEFT = 22L;
  private static long datacenterId = 1L;
  private static long machineId = 1L;
  private static long sequence = 0L;
  private static long lastStmp = -1L;
  
  public SnowFlakeGenerator(long datacenterId, long machineId)
  {
    if ((datacenterId > 31L) || (datacenterId < 0L)) {
      throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
    }
    if ((machineId > 31L) || (machineId < 0L)) {
      throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
    }
    datacenterId = datacenterId;
    machineId = machineId;
  }
  
  public static synchronized long nextId()
  {
    long currStmp = getNewstmp();
    if (currStmp < lastStmp) {
      throw new RuntimeException("Clock moved backwards.  Refusing to generate id");
    }
    if (currStmp == lastStmp)
    {
      sequence = sequence + 1L & 0xFFF;
      if (sequence == 0L) {
        currStmp = getNextMill();
      }
    }
    else
    {
      sequence = 0L;
    }
    lastStmp = currStmp;
    
    return currStmp - 1480166465631L << 22 | datacenterId << 17 | machineId << 12 | sequence;
  }
  
  private static long getNextMill()
  {
    long mill = getNewstmp();
    while (mill <= lastStmp) {
      mill = getNewstmp();
    }
    return mill;
  }
  
  private static long getNewstmp()
  {
    return System.currentTimeMillis();
  }
  
  public static void main(String[] args)
  {
    SnowFlakeGenerator snowFlake = new SnowFlakeGenerator(1L, 1L);
    
    long start = System.currentTimeMillis();
    System.out.println(nextId());
    
    System.out.println(System.currentTimeMillis() - start);
  }
}
